#include <termios.h>

#include "main.h"

void echo_on(void);
void echo_off(void);

static struct termios stored_settings;

extern Z80EX_BYTE memory[MEMORY_SIZE];

/* Entered with C=1. Returns A=L=character. */
void console_input(Z80EX_CONTEXT *cpu, Z80EX_BYTE c, Z80EX_WORD de)
{
	int ch = getchar();
	
	z80_set_high(cpu, regAF, ch & 0xFF);
	z80_set_low(cpu, regHL, ch & 0xFF);
}

/* Entered with C=2, E=ASCII character. */
void console_output(Z80EX_CONTEXT *cpu, Z80EX_BYTE c, Z80EX_WORD de)
{
	putchar(de & 0xFF);
	fflush(stdout);
}

/* Entered with C=6, E=code. Returned values (in A) vary.
 * E=0FFh
    Return a character without echoing if one is waiting; zero if none is available.
 * E=0FEh
    Test keyboard status. Return 0 if no key is pressed otherwise return non-zero.
 * Values of E not supported on a particular system will output the character.
*/
void direct_console_io(Z80EX_CONTEXT *cpu, Z80EX_BYTE c, Z80EX_WORD de)
{
	Z80EX_BYTE e = de & 0xFF;
	if (e == 0xFF || e == 0xFE)
	{
		// Input
		
		echo_off();
		int ch = getchar();
		if (ch == -1)
			z80_set_high(cpu, regAF, 0);
		else
			if (e == 0xFE)
			{
				// We should only test keyboard buffer, so return char back to it
				ungetc(ch, stdin);
				z80_set_high(cpu, regAF, 1);
			}
			else
				z80_set_high(cpu, regAF, ch & 0xFF);
		echo_on();
	}
	else
	{
		// Output
		putchar(de & 0xFF);
		fflush(stdout);
	}
}

/* Entered with C=9, DE=address of string. */
void output_string(Z80EX_CONTEXT *cpu, Z80EX_BYTE c, Z80EX_WORD de)
{
	int i = de;
	while(memory[i] != '$' && i < MEMORY_SIZE)
	{
		putchar(memory[i++]);
	}
	fflush(stdout);
}

/*
 * Entered with C=0Ah, DE=address or zero.
 * This function reads characters from the keyboard into a memory buffer until RETURN is pressed. 
*/
void buffered_console_input(Z80EX_CONTEXT *cpu, Z80EX_BYTE c, Z80EX_WORD de)
{
	char buffer[257];
	fgets(buffer, 257, stdin);

	Z80EX_BYTE max_count = memory[de];
	int i = 0;
	for (i = 0; i < max_count; i++)
	{
		if (buffer[i] == 0)
			break;
		memory[de + 2 + i] = buffer[i];
	}
	memory[de + 1] = i;
}

/* Entered with C=0Bh. Returns A=L=status
Returns A=0 if no characters are waiting, nonzero if a character is waiting.
*/
void console_status(Z80EX_CONTEXT *cpu, Z80EX_BYTE c, Z80EX_WORD de)
{
	echo_off();
	int ch = getchar();
	if (ch == -1)
		z80_set_high(cpu, regAF, 0);
	else
	{
		ungetc(ch, stdin);
		z80_set_high(cpu, regAF, 1);
	}
	echo_on();
}

void echo_off(void)
{
    struct termios new_settings;
    tcgetattr(0, &stored_settings);
    new_settings = stored_settings;
    new_settings.c_lflag &= (~ICANON);
    new_settings.c_lflag &= (~ECHO);
    new_settings.c_cc[VTIME]=0;
    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    return;
}

void echo_on(void)
{
    tcsetattr(0, TCSANOW, &stored_settings);
    return;
}
